package com.bugreport.configuration;

import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.bugreport.utils.BugReportException;

public class BugTrackerConfigurationLoader implements ApplicationContextAware, InitializingBean {

	// TODO allow reloading configuration after hot modification

	private static final Logger logger = Logger.getLogger(BugTrackerConfigurationLoader.class);

	private ApplicationContext context;
	private Properties properties;
	private Map<Integer, BugTrackerConfiguration> configurations;

	public void setConfigurationProperties(Properties properties) {
		this.properties = properties;
	}

	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.context = context;
	}

	public void afterPropertiesSet() throws Exception {
		logger.debug("Loader instantiated.");
		load();
	}

	protected Map<Integer, BugTrackerConfiguration> getConfigurations() {
		return configurations;
	}

	public void load() {

		logger.debug("Start configuration loading...");

		if (context == null || properties == null) {

			logger.fatal("Configuration Loader has not been well instantiated.");
			logger.fatal("Configurations can not be loaded and application can not be started.");
			logger.fatal("You should not see this message with production version of this application.");
			logger.fatal("Please report issue to administrator.");

		} else {

			try {

				this.configurations = load(context, properties);

				if (configurations.size() == 0) {

					logger.warn("No configuration found in property file: issues will not be reported.");
					logger.warn("You should add configurations as documented on ");
					// FIXME add web site URL to configuration documentation web page or add link to embedded
					// documentation on local server

				} else {

					logger.info("Configurations loaded (" + configurations.size() + " found).");
				}

			} catch (BugReportException ex) {

				this.configurations = null;

				logger.fatal("Configuration loading has failed due to following errors.");
				logger.fatal("Please solve errors before redeploying application.");
				logger.fatal(ex.getCode() + ":" + ex.getMessage(), ex);
			}
		}
	}

	protected static Set<Integer> extractConfigurationIds(Properties properties) throws BugReportException {

		Set<Integer> ids = new HashSet<Integer>();

		for (Object key : properties.keySet()) {

			String[] keyParts = ((String) key).split("\\.");

			if (keyParts.length < 2) {
				throw new BugReportException(100, (String) key);
			}

			try {
				ids.add(Integer.valueOf(keyParts[0]));
			} catch (NumberFormatException ex) {
				throw new BugReportException(101, keyParts[0], (String) key);
			}
		}

		return ids;
	}

	protected static Map<Integer, BugTrackerConfiguration> load(ApplicationContext context, Properties properties)
			throws BugReportException {

		Map<Integer, BugTrackerConfiguration> configs = new TreeMap<Integer, BugTrackerConfiguration>();

		for (Integer configId : extractConfigurationIds(properties)) {
			configs.put(configId, load(context, properties, configId));
		}

		return configs;
	}

	protected static BugTrackerConfiguration load(ApplicationContext context, Properties properties, Integer configId)
			throws BugReportException {

		String bugTrackerKey = BugTrackerConfiguration.getBugTrackerKey(properties, configId);
		String beanId = bugTrackerKey.concat(BugTrackerConfiguration.SPRING_BEAN_SUFFIX);

		if (context.containsBean(beanId)) {
			return (BugTrackerConfiguration) context.getBean(beanId, new Object[] { properties, configId });
		} else {
			throw new BugReportException(102, bugTrackerKey);
		}
	}

	public BugTrackerConfiguration getMatchingConfiguration(String url) throws BugReportException {

		for (BugTrackerConfiguration configuration : configurations.values()) {
			if (configuration.isUrlMatching(url)) {
				logger.info("URL {" + url + "} matches configuration #" + configuration.getIdConfig() + " ("
						+ configuration.getBugTrackerKey() + ")");
				return configuration;
			}
		}

		throw new BugReportException(103, url);
	}
}
